package com.ingenic.glass.camera.live;

import android.app.Activity;
import android.hardware.Camera;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.BatteryManager;
import android.os.Environment;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.GestureDetector;
import android.widget.GestureDetector.SimpleOnGestureListener;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;

import java.io.IOException;

import com.ingenic.glass.voicerecognizer.api.VoiceRecognizer;
import com.ingenic.glass.voicerecognizer.api.VoiceRecognizerListener;
import com.ingenic.glass.camera.*;
import com.ingenic.glass.camera.util.Util;

public class CameraRTMPLive extends Activity implements SurfaceHolder.Callback,
		MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener
							, PictureCallback{
	private final String TAG = "CameraRTMPLive";
	private final boolean NO_SCREEN_DEVICE = CameraAppImpl.NO_SCREEN_DEVICE;
	// Send message to GlassAVServer.apk
	public static final String CAMERA_ACTION_STATUS = "com.ingenic.glass.camera.rtmplive.STATUS";
	// JNI message, consistent with definition in
	// frameworks/av/media/libstagefright/LiveWriter.cpp
	private final int MEDIA_RECORDER_TRACK_INFO_LIVE_SERVER_START = 1900;
	private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
	private final int OUTPUT_FORMAT_LIVE_RTMP = 12; // see media/mediarecorder.h
	private static CameraRTMPLive mInstance = null;
	private ComboPreferences mPreferences;
	private SurfaceHolder mSurfaceHolder = null;
	private boolean mOpenCameraFail = false;
	private MediaRecorder mMediaRecorder;
	private boolean mMediaRecorderRecording = false;
	// The video file that the hardware camera is about to record into
	// (or is recording into.)
	private CamcorderProfile mProfile;
	private boolean mCaptureTimeLapse = false;
	// Default 0. If it is larger than 0, the camcorder is in time lapse mode.
	private int mTimeBetweenTimeLapseFrameCaptureMs = 0;
	private int mDesiredPreviewWidth;
	private int mDesiredPreviewHeight;
	private Parameters mParameters;
	private int mCameraId;
	private GestureDetector mGestureDetector;
	private Object mLock = new Object();
	private boolean mFinished = false;
	private Camera mCameraDevice;

	// check incall state and add tts
	private AudioManager mAudioManager;
	private VoiceRecognizer mVoiceRecognizer = null;
	private CameraAppImpl mApplication;
	private String mCurrentErrorState;

	public static final String STATUS_START_LIVE = "start_live";
	public static final String STOP_START_LIVE = "stop_live";

	private Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if (CameraAppImpl.DEBUG)
				Log.d(TAG, "handleMessage in msg.what=" + msg.what);
			switch (msg.what) {
			case CameraAppImpl.CAMERA_ERROR_PREVIEW: {
				if (mVoiceRecognizer != null)
					mVoiceRecognizer
							.playTTS(getString(R.string.tts_live_video_record_error));
				mCurrentErrorState = getString(R.string.video_record_error);
				finish();
				break;
			}
			}
		}
	};

	private BroadcastReceiver mReceiver = null;

	private class MyBroadcastReceiver extends BroadcastReceiver {
		@Override
		public void onReceive(Context context, Intent intent) {
			if (intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
				int currentBatteryVoltage = intent.getIntExtra(
						BatteryManager.EXTRA_VOLTAGE,
						CameraAppImpl.LOWEST_BATTERY_VOLTAGE);
				if (CameraAppImpl.DEBUG)
					Log.d(TAG, "battery has change :: currentBatteryVoltage= "
							+ currentBatteryVoltage);
				if (currentBatteryVoltage <= CameraAppImpl.LOWEST_BATTERY_VOLTAGE) {
					mCurrentErrorState = getString(R.string.live_error_low_battery);
					finish();
				}
			}else if (intent.getAction().equals(Intent.ACTION_MEDIA_EJECT)) {
				stopVideoRecording();
				finish();
			}
		}
	}

	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		if (CameraAppImpl.DEBUG)
			Log.d(TAG, "onCreate in");
		CameraRTMPLive.setMInstance(this);
		mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
		mCurrentErrorState = STOP_START_LIVE;
		mVoiceRecognizer = new VoiceRecognizer(VoiceRecognizer.REC_TYPE_COMMAND,
						       new VoiceRecognizerListener());
		mVoiceRecognizer.setAppName(getString(R.string.camera_live_label));
		mVoiceRecognizer.register();
		if (!checkEnvironmentState()) {
			finish();
			return;
		}
		setContentView(R.layout.camera_live);
		SurfaceView sv = (SurfaceView) findViewById(R.id.surfaceview);
		SurfaceHolder holder = sv.getHolder();
		holder.addCallback(this);
		holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		mGestureDetector = new GestureDetector(this,
				new MySimpleGestureDetectorListener());
		mMediaRecorder = new MediaRecorder();
		mMediaRecorder.setOnErrorListener(this);
		mMediaRecorder.setOnInfoListener(this);
		mApplication = (CameraAppImpl) getApplication();
		init();
		if (mOpenCameraFail) {
			mCurrentErrorState = getString(R.string.video_record_error);
			finish();
			return;
		}
		Settings.System.putString(this.getContentResolver(), "camera_state",
				"live");
		if (CameraAppImpl.USE_TTS && mAudioManager.getMode() != AudioManager.MODE_IN_CALL) {
			mVoiceRecognizer.playTTS(getString(R.string.tts_live_video_record_start));
		}
		mVoiceRecognizer.lockRecognizer();
	}

	private boolean checkEnvironmentState() {
		int currentBatteryVoltage = 0;
		try {
			currentBatteryVoltage = Settings.System.getInt(
					getContentResolver(), "batteryVoltage");
		} catch (SettingNotFoundException e) {
			Log.e(TAG, "batteryVoltage -- SettingNotFound");
		}

		if (currentBatteryVoltage <= CameraAppImpl.LOWEST_BATTERY_VOLTAGE) {
			mCurrentErrorState = getString(R.string.live_error_low_battery);
			return false;
		} else if (mAudioManager.getMode() == AudioManager.MODE_IN_CALL) {
			mCurrentErrorState = getString(R.string.not_support_live_with_audio);
			return false;
		}
		return true;
	}

	private void init() {
		Util.initializeScreenBrightness(getWindow(), getContentResolver());
		mPreferences = new ComboPreferences(this);
		CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
		mCameraId = CameraSettings.readPreferredCameraId(mPreferences);
		if (CameraAppImpl.DEBUG)
			Log.d(TAG, "---mCameraId=" + mCameraId);
		// Testing purpose. Launch a specific camera through the intent extras.
		int intentCameraId = Util.getCameraFacingIntentExtras(this);
		if (intentCameraId != -1) {
			mCameraId = intentCameraId;
		}

		mPreferences.setLocalId(this, mCameraId);
		CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());

		// Register BroadcastReceiver to handle some event
		IntentFilter intentFilter = new IntentFilter(
				Intent.ACTION_BATTERY_CHANGED);
		intentFilter.addAction(Intent.ACTION_MEDIA_EJECT);
		mReceiver = new MyBroadcastReceiver();
		registerReceiver(mReceiver, intentFilter);

		/*
		 * To reduce startup time, we start the preview in another thread. We
		 * make sure the preview is started at the end of onCreate.
		 */
		Thread startPreviewThread = new Thread(new Runnable() {
			public void run() {
				try {
					if (CameraAppImpl.DEBUG)
						Log.d(TAG, "---opencamera mCameraId=" + mCameraId);
					mCameraDevice = Util.openCamera(CameraRTMPLive.this,
							mCameraId);
					if (mCameraDevice == null) {
						mOpenCameraFail = true;
					} else {
						mCameraDevice.setErrorCallback(mErrorCallback);
						mErrorCallback.setHandler(mHandler);
					}
				} catch (Exception e) {
					mOpenCameraFail = true;
				}
			}
		});
		startPreviewThread.start();
		Util.enterLightsOutMode(getWindow());
		// Make sure preview is started.
		try {
			startPreviewThread.join();
		} catch (InterruptedException ex) {
			// ignore
		}
	}

	private void readVideoPreferences(boolean hasAudio) {
		// The preference stores values from ListPreference and is thus string
		// type for all values.
		// We need to convert it to int manually.
		String defaultQuality = CameraSettings.getDefaultVideoQuality(
				mCameraId,
				getResources().getString(R.string.pref_video_quality_default));
		String videoQuality = mPreferences.getString(
				CameraSettings.KEY_VIDEO_QUALITY, defaultQuality);
		int quality = Integer.valueOf(videoQuality);
		if (!hasAudio) {
			mTimeBetweenTimeLapseFrameCaptureMs = 40;
			mCaptureTimeLapse = true;
		} else {
			mTimeBetweenTimeLapseFrameCaptureMs = 0;
			mCaptureTimeLapse = false;
		}
		// TODO: This should be checked instead directly +1000.
		if (mCaptureTimeLapse)
			quality += 1000;
		mProfile = CamcorderProfile.get(mCameraId, quality);
		// mProfile.fileFormat = OUTPUT_FORMAT_LIVE_RTMP;
		getDesiredPreviewSize();
	}

	private void getDesiredPreviewSize() {
		if (mCameraDevice == null)
			return;
		mParameters = mCameraDevice.getParameters();
		mDesiredPreviewWidth = mProfile.videoFrameWidth;
		mDesiredPreviewHeight = mProfile.videoFrameHeight;
		if(CameraAppImpl.PENQUANYUN_LIVE){
			mDesiredPreviewWidth = 352;
			mDesiredPreviewHeight = 640;
		} else{
			mDesiredPreviewWidth = 640;
			mDesiredPreviewHeight = 352;
		}
	}

	private void closeCamera() {
		if (CameraAppImpl.DEBUG)
			Log.d(TAG, "closeCamera");
		if (mCameraDevice == null) {
			return;
		}
		mCameraDevice.stopPreview();
		CameraHolder.instance().release();
		mCameraDevice.setErrorCallback(null);
		mCameraDevice = null;
	}

	private void finishRecorderAndCloseCamera() {
		if (CameraAppImpl.DEBUG)
			Log.d(TAG, "-------stopVideoRecording in");
		stopVideoRecording();
		closeCamera();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		if (CameraAppImpl.DEBUG)
			Log.d(TAG, "onDestroy in");
		mVoiceRecognizer.unlockRecognizer();
		Settings.System.putString(this.getContentResolver(), "camera_state", "");
		Intent intent = new Intent(CAMERA_ACTION_STATUS);
		intent.putExtra("status", mCurrentErrorState);
		sendBroadcast(intent);
		if (mVoiceRecognizer != null)
			mVoiceRecognizer.unRegister();
		if (mApplication != null)
			mApplication.releaseWakeLock();
		if (mReceiver != null) {
			unregisterReceiver(mReceiver);
			mReceiver = null;
		}
		CameraRTMPLive.setMInstance(null);
	}

	// Prepares media recorder.
	private void initializeRecorder() {
		// If the mCameraDevice is null, then this activity is going to finish
		if (CameraAppImpl.DEBUG)
			Log.d(TAG, "mCameraDevice = " + mCameraDevice
					+ " mSurfaceHolder = " + mSurfaceHolder);
		if (mCameraDevice == null || mSurfaceHolder == null)
			return;
		// Unlock the camera object before passing it to media recorder.
		mCameraDevice.unlock();
		mMediaRecorder.setCamera(mCameraDevice);
		if (!mCaptureTimeLapse) {
			mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
		}

		// mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
		// mMediaRecorder.setProfile(mProfile);

		mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
		mMediaRecorder.setOutputFormat(OUTPUT_FORMAT_LIVE_RTMP);
		mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
		mMediaRecorder.setVideoFrameRate(15);
		mMediaRecorder.setVideoSize(mDesiredPreviewWidth, mDesiredPreviewHeight);
		mMediaRecorder.setVideoEncodingBitRate(6000000);
		mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
		mMediaRecorder.setAudioEncodingBitRate(12200);
		mMediaRecorder.setAudioChannels(2);
		mMediaRecorder.setAudioSamplingRate(16000);
		// set RTMP URL
		//mMediaRecorder.setLiveRtmpUrl("rtmp://sp1.vcloud.360.cn/live_huajiao_v2/altotech19");
		String url = getIntent().getStringExtra("liveUrl");
		Log.i(TAG,"liveUrl="+url);
		if(url != null && url.length() != 0)
		    mMediaRecorder.setLiveRtmpUrl(url);
		if (mCaptureTimeLapse) {
			mMediaRecorder.setCaptureRate((1000 / (double) mTimeBetweenTimeLapseFrameCaptureMs));
		}
		mMediaRecorder.setOutputFile(Storage.BASE_DIR + '/' + "camerartmplive");
		mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
		try {
			mMediaRecorder.prepare();
		} catch (IOException e) {
			releaseMediaRecorder();
			throw new RuntimeException(e);
		}
	}

	private void releaseMediaRecorder() {
		if (mMediaRecorder != null) {
			mMediaRecorder.reset();
			mMediaRecorder.release();
			mMediaRecorder = null;
		}
	}

	// from MediaRecorder.OnErrorListener
	@Override
	public void onError(MediaRecorder mr, int what, int extra) {
		Log.e(TAG, "MediaRecorder error. what=" + what + " extra=" + extra);
		mCurrentErrorState = getString(R.string.video_record_error);
		finish();
	}

	// from MediaRecorder.OnInfoListener
	@Override
	public void onInfo(MediaRecorder mr, int what, int extra) {
		if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
			if (mMediaRecorderRecording)
				stopVideoRecording();
		} else if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
			if (mMediaRecorderRecording)
				stopVideoRecording();
		} else if (what == MEDIA_RECORDER_TRACK_INFO_LIVE_SERVER_START) {
			Intent intentStatus = new Intent(CAMERA_ACTION_STATUS);
			intentStatus.putExtra("status", STATUS_START_LIVE);
			sendBroadcast(intentStatus);
		}
	}

	private void startVideoRecording() {
		if (mMediaRecorderRecording || mFinished)
			return;
		readVideoPreferences(mAudioManager.getMode() != AudioManager.MODE_IN_CALL);
		setCameraParameters();
		if (CameraAppImpl.DEBUG)
			Log.d(TAG, "startVideoRecording");
		initializeRecorder();
		if (mMediaRecorder == null) {
			Log.e(TAG, "Fail to initialize media recorder");
			return;
		}
		try {
			mMediaRecorder.start(); // Recording is now started
		} catch (RuntimeException e) {
			Log.e(TAG, "Could not start media recorder. ", e);
			releaseMediaRecorder();
			// If start fails, frameworks will not lock the camera for us.
			mCameraDevice.lock();
			return;
		}
		mMediaRecorderRecording = true;
	}

	private void stopVideoRecording() {
		if (CameraAppImpl.DEBUG)
			Log.d(TAG, "stopVideoRecording");
		if (mMediaRecorderRecording) {
			try {
				mMediaRecorder.setOnErrorListener(null);
				mMediaRecorder.setOnInfoListener(null);
				mMediaRecorder.stop();
			} catch (RuntimeException e) {
				Log.e(TAG, "stop fail", e);
			}
			mMediaRecorderRecording = false;

		}
		releaseMediaRecorder();
	}

	@Override
	public void finish() {
		synchronized (mLock) {
			if (mFinished)
				return;
			mFinished = true;
		}
		// 及时closecamera，否则第二次开启会失败，
		// 原先是在onDestroy中做，而onDestroy调用较晚如果在onDestroy
		// 调用之前开启录像就会出错，所以把closecamera提前到finish时做
		finishRecorderAndCloseCamera();
		if (CameraAppImpl.USE_TTS && mAudioManager.getMode() != AudioManager.MODE_IN_CALL) {
			String tts = null;
			if (mCurrentErrorState.equals(STOP_START_LIVE)) {
				tts = getString(R.string.tts_live_video_record_stop);
			} else {
				tts = getString(R.string.tts_live_video_record_error);
			}
			if (mVoiceRecognizer != null)
				mVoiceRecognizer.playTTS(tts);
		}

		super.finish();
	}

	private void setCameraParameters() {
		if (CameraAppImpl.DEBUG)
			Log.d(TAG, "set camera_ipu_direct record and restart preview.");
		if (mCameraDevice == null)
			return;
		if (NO_SCREEN_DEVICE)
			mParameters.set("preview_mode", CameraAppImpl.NO_SCREEN_LIVE);
		else
			mParameters.set("preview_mode",
					CameraAppImpl.LOW_POWER_DISPLAY_VIDEO_LIVE);
		mParameters.setPreviewSize(mDesiredPreviewWidth, mDesiredPreviewHeight);
		mCameraDevice.setParameters(mParameters);
		mApplication.acquireWakeLock();
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		return mGestureDetector.onTouchEvent(event);
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
		if (CameraAppImpl.DEBUG)
			Log.d(TAG, "holder.getSurface() = " + holder.getSurface()
					+ "  mCameraDevice = " + mCameraDevice);
		if (holder.getSurface() == null || mCameraDevice == null)
			return;
		mSurfaceHolder = holder;
		startVideoRecording();
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		if (CameraAppImpl.DEBUG)
			Log.d(TAG, "--surfaceDestroyed");
		mSurfaceHolder = null;
	}

	class MySimpleGestureDetectorListener extends
			GestureDetector.SimpleOnGestureListener {
		@Override
		public boolean onSlideDown(boolean fromPhone) {
			mCurrentErrorState = STOP_START_LIVE;
			finish();
			return true;
		}
	}

	public synchronized static void setMInstance(CameraRTMPLive mInstance) {
		CameraRTMPLive.mInstance = mInstance;
	}

	public synchronized static CameraRTMPLive getMInstance() {
		return CameraRTMPLive.mInstance;
	}

	public void setErrorState(String state) {
		mCurrentErrorState = state;
	}

        public void takePicture() {
	    if (mCameraDevice == null) {
		return ;
	    }
	    mCameraDevice.takePicture(null, null, null, this);
	}
    
        @Override
	public void onPictureTaken(final byte [] jpegData, final android.hardware.Camera camera) {
	    Util.storeImage(this,jpegData,mParameters.getPictureSize());
	}
}
